module csr(
    input clk,
    input reset,
    input [63:0] csr_wdata,
    input [11:0] i_r_csr,
    input [11:0] i_w_csr,
    input write_ena,
    input except_ena,
    //input ext_int,
    input [63:0] i_addr,
    input [63:0] i_addr2,
    input [63:0] i_badvaddr,
    input [63:0] i_excode,
    input [31:0] badinst,
    input ret,
    input mtime_int,
    output reg [63:0] o_csr_data,
    output [63:0] mtvec_pc,
    output [63:0] epc,
    output [63:0] mstatus_data,
    output [63:0] mtvec_data,
    output [63:0] mepc_data,
    output [63:0] mcause_data,
    output [63:0] mie_data,
    output [63:0] mip_data,
    output [63:0] mscratch_data,
    output [63:0] sstatus_data,
    output o_mtime_int
);

    wire ext_int = 1'b0;
    reg [63:0] mcpuid;
    reg [63:0] mimpid;
    reg [63:0] mhartid;
    reg [63:0] mstatus;
    reg [63:0] mtvec;
    reg [63:0] mepc;
    reg [63:0] mcause;
    reg [63:0] mie;
    reg [63:0] mip;
    reg [63:0] mtval;
    reg [63:0] mscratch;
    reg [63:0] mcycle;

    assign o_mtime_int = mtime_int && mie[7] == 1'b1 && mstatus[3] == 1'b1 ? 1'b1 : 1'b0;

    assign mstatus_data = mstatus;
    assign mtvec_data = mtvec;
    assign mepc_data = mepc;
    assign mcause_data = mcause;
    assign mie_data = mie;
    assign mip_data = mip;
    assign mscratch_data = mscratch;
    assign sstatus_data = mstatus_data & 64'h80000003000DE122;
    

    always @(posedge clk) begin
        if(reset) begin
            mcpuid <= {2'b10, 36'd0, 17'b0, 1'b1, 8'd0};
        end else if(write_ena && i_w_csr == 12'hf00) begin
            mcpuid <= csr_wdata;
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            
        end else if(write_ena && i_w_csr == 12'hf01) begin
            mimpid <= csr_wdata;
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            mhartid <= 64'd0;
        end else if(write_ena && i_w_csr == 12'hf10) begin
            mhartid <= csr_wdata;
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            mstatus <= {51'd0, 2'b11, 3'b000, 1'b1, 3'b000, 1'b1, 3'b000};
        end else if((except_ena || mtime_int && mie[7] == 1'b1) && mstatus[3] == 1'b1) begin
            mstatus <= {mstatus[63:13], 2'b11, 3'b000, mstatus[3], 3'b000, 1'b0, 3'b000};
        end else if(write_ena && i_w_csr == 12'h300) begin
            mstatus <= {csr_wdata[14:13] == 2'b11 || csr_wdata[16:15] == 2'b11, csr_wdata[62:0]};
        end else if(ret) begin
            mstatus <= {mstatus[63:13], 2'b00, 3'b000, 1'b1, 3'b000, mstatus[7], 3'b000};
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            mtvec <= 64'd0;
        end else if(write_ena && i_w_csr == 12'h305) begin
            mtvec <= csr_wdata;
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            mepc <= 64'd0;
        end else if(mtime_int && mstatus[3] == 1'b1 && mie[7] == 1'b1) begin
            mepc <= {i_addr2[63:2], 2'b00};
        end else if(except_ena) begin
            mepc <= {i_addr[63:2], 2'b00};
        end else if(write_ena && i_w_csr == 12'h341) begin
            mepc <= csr_wdata;
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            mcause <= 64'd0;
        end else if(ext_int) begin
            mcause <= {1'b1, 63'd11};
        end else if(mtime_int && mstatus[3] == 1'b1 && mie[7] == 1'b1) begin
            mcause <= {1'b1, 63'd7};
        end else if(except_ena) begin
            mcause <= i_excode;
        end else if(write_ena && i_w_csr == 12'h342) begin
            mcause <= csr_wdata;
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            mie <= {52'd0, 1'b0, 3'b000, 1'b0, 3'b000, 1'b0, 3'b000};
        end else if(write_ena && i_w_csr == 12'h304) begin
            mie <= csr_wdata;
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            mip <= {52'd0, 1'b0, 3'b000, 1'b0, 3'b000, 1'b0, 3'b000};
        end else if(write_ena && i_w_csr == 12'h344) begin
            mip <= csr_wdata;
        end else begin
            if(ext_int) begin
                mip[11] <= 1'b1;
            end else begin
                mip[11] <= 1'b0;
            end
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            mtval <= 64'd0;
        end else if(i_excode[63] == 0 &&(i_excode[62:0] == 63'd0 || i_excode[62:0] == 63'd1 || i_excode[62:0] == 63'd3 || i_excode[62:0] == 63'd4 || i_excode[62:0] == 63'd5 || i_excode[62:0] == 63'd6 || i_excode[62:0] == 63'd7)) begin
            mtval <= i_badvaddr;
        end else if(i_excode[63] == 0 && i_excode[62:0] == 63'd2) begin
            mtval <= {32'd0, badinst};
        end else begin
            mtval <= 64'd0;
        end
    end

    always @(posedge clk) begin
        if(reset) begin
            mscratch <= 64'd0;
        end else if(write_ena && i_w_csr == 12'h340) begin
            mscratch <= csr_wdata;
        end
    end


    always @(posedge clk) begin
        if(reset) begin
            mcycle <= 64'd0;
        end else if(write_ena && i_w_csr == 12'hb00) begin
            mcycle <= csr_wdata;
        end
        mcycle <= mcycle + 64'd1;
    end

    assign mtvec_pc = mtvec[1:0] == 2'b00 ? {mtvec[63:2], 2'b00} : (mcause[63] == 0 ? {mtvec[63:2], 2'b00} + mcause[62:0] >> 2 : (mcause[63] == 1 ? {mtvec[63:2], 2'b00} : 64'd0));
    assign epc = mepc;
    
    always @(i_r_csr) begin
        case(i_r_csr)
            12'hf13 : begin
                o_csr_data <= mimpid;
            end
            12'h300 : begin
                o_csr_data <= mstatus;
            end
            12'h304 : begin
                o_csr_data <= mie;
            end
            12'h305 : begin
                o_csr_data <= mtvec;
            end
            12'h340 : begin
                o_csr_data <= mscratch;
            end
            12'h341 : begin
                o_csr_data <= mepc;
            end
            12'h342 : begin
                o_csr_data <= mcause;
            end
            12'h343 : begin
                o_csr_data <= mtval;
            end
            12'h344 : begin
                o_csr_data <= mip;
            end
            12'hf14 : begin
                o_csr_data <= mhartid;
            end
            12'hb00 : begin
                o_csr_data <= mcycle;
            end
            default : begin
                
            end
        endcase
    end

endmodule